#pragma once

#include "Sock.hpp"
#include "Epoll.hpp"
#include "Util.hpp"
#include "Cus_protocol.hpp"
#include <functional>
#include <cerrno>

#define BLACKLOG 20
#define EVENTS_NUM 128 // 就绪列表数量

class Connection;
class EpollServer;
using CallBack_Func = std::function<int(Connection *)>;
using handler_func = std::function<int(Connection *, string &)>;
using service_func = std::function<void(Protocol::Reponse *, const Protocol::Requst&)>;

//==============================================================================================================

class Connection
{
public:
    Connection(int fd, EpollServer *This)
        : fd_(fd), this_(This)
    {
    }

    ~Connection()
    {
    }

    // 设置不同事件就绪的回调处理方法 -- 由外部用户进行设置
    void SetRecvEvent(CallBack_Func recvEven) { recvEvent_ = recvEven; }
    void SetSendEvent(CallBack_Func sendEvent) { sendEvent_ = sendEvent; }
    void SetexcepEvent(CallBack_Func excepEvent) { excepEvent_ = excepEvent; }

public:
    // 获取不同事件就绪的回调处理方法
    CallBack_Func GetRecvEvent() const { return recvEvent_; }
    CallBack_Func GetSendEvent() const { return sendEvent_; }
    CallBack_Func GetExcepEvent() const { return excepEvent_; }

public:
    int Getfd() const { return fd_; }
    EpollServer *GetepollServerThis() { return this_; }
    string &GetInbuffer() { return inbuffer_; }
    string &GetOutbuffer() { return outbuffer_; }

private:
    int fd_;
    // 三种就绪事件的回调处理方法 -- 读事件方法、写事件方法、异常事件方法
    CallBack_Func recvEvent_;
    CallBack_Func sendEvent_;
    CallBack_Func excepEvent_;
    // 接受和发送缓冲区
    string inbuffer_;
    string outbuffer_;
    EpollServer *this_;
};

//==============================================================================================================

class EpollServer
{
public:
    EpollServer(handler_func hdf, uint16_t port, string ip = "")
        : hdf_(hdf), port_(port), ip_(ip), listenSockfd_(-1), epfd_(-1)
    {
    }

    ~EpollServer()
    {
        if (listenSockfd_ > STDERR_FILENO)
            close(listenSockfd_);

        if (epfd_ > listenSockfd_)
            close(epfd_);

        // 因为哈希表中的映射是动态开辟的Connection类对象，析构要delete
        for (auto &KV : Connections_)
            delete (KV.second);
    }

    //==============================================================================================================

    void InitEpollServer()
    {
        // 1.创建sock
        listenSockfd_ = Sock::Socket();
        // 1.1设置 listenSockfd_ 为非阻塞式状态
        Util::SetNoBlock(listenSockfd_);
        // 1.2绑定ip、port到内核
        Sock::Bind(listenSockfd_, ip_, port_);
        // 1.3设置sock为监听连接请求状态
        Sock::Listen(listenSockfd_, BLACKLOG);

        // 2.创建epoll模型 -- 在内核创建回调机制、红黑树和就绪队列
        epfd_ = Epoll::CreateEpoll();

        // 3.把listenSockfd_添加到epoll模型中进行事件监听 -- epoll_ctl(构建RB_Tree Node, 插入到RB_Tree中)
        Epoll::AddEvent(epfd_, listenSockfd_, EPOLLIN | EPOLLET); // 设置读事件监听和ET工作方式

        // 4.将listenSockfd_匹配的Connection添加到Connections中
        Connections_[listenSockfd_] = new Connection(listenSockfd_, this);
        // 5.设置listenSockfd_的回调 -- 读就绪后获取连接到应用层，后面放到epoll模型进行事件监听+建立Connetctions映射
        Connections_[listenSockfd_]->SetRecvEvent(std::bind(&EpollServer::Accepter, this, std::placeholders::_1));

        // 6.Init evEnts -- 初始化就绪事件列表 -- 充当缓冲区获取就绪队列的值
        epoll_event evt;
        evt.data.fd = 0;
        evt.events = 0;
        evEnts_.resize(EVENTS_NUM, evt);
    }

    // 启动服务器
    void StartEpollServer()
    {
        while (true)
        {
            Dispatcher();
        }
    }

    //================================================================================================================

private:
    // 建立fd和用户网络基本信息的映射(fd => ip:port)
    void EstUserDataMap(int fd, const string &ClientIp, const uint16_t ClientPort)
    {
        string UserData = ClientIp;
        UserData += ':';
        UserData += std::to_string(ClientPort);
        UserInetWorkData_[fd] = UserData;
    }

    // 添加新的fd到epoll模型进行事件监听、建立新的映射关系(fd => Connection)
    void AddConnection(int serverfd, uint32_t eventType)
    {
        // 1.设置serverfd为非阻塞式状态
        Util::SetNoBlock(serverfd);
        // 2.将新的fd添加到epoll模型中
        Epoll::AddEvent(epfd_, serverfd, eventType);
        // 3.建立新的fd和Connection的映射关系
        Connections_[serverfd] = new Connection(serverfd, this);

        // 4.设置服务IO fd对应的读、写、和异常事件就绪处理方法
        Connections_[serverfd]->SetRecvEvent(std::bind(&EpollServer::RecvEvent, this, std::placeholders::_1));
        Connections_[serverfd]->SetSendEvent(std::bind(&EpollServer::SendEvent, this, std::placeholders::_1));
        Connections_[serverfd]->SetexcepEvent(std::bind(&EpollServer::ExceptEvent, this, std::placeholders::_1));
    }

    int Accepter(Connection *connect)
    {
        // 在ET工作模式下的listenSockfd_，可能一次性到来多个连接请求，需要循环式的获取新连接
        while (true)
        {
            // 1.获取新连接
            string ClientIp;
            uint16_t ClientPort;
            int serverfd = Sock::Accept(connect->Getfd(), &ClientIp, &ClientPort);
            // 2.添加到epoll模型进行事件监听
            if (serverfd > 0)
            {
                AddConnection(serverfd, EPOLLIN | EPOLLET);
                EstUserDataMap(serverfd, ClientIp, ClientPort);

                cout << "添加新fd到epoll成功, serverfd: " << serverfd << ", " << UserInetWorkData_[serverfd] << endl; // Debug
            }
            else
            {
                if (EAGAIN == errno || EWOULDBLOCK == errno) // 没有可用的连接可用获取
                    break;
                else if (errno == EINTR) // 获取连接被信号中断
                    continue;
                else // 连接获取异常
                {
                    std::cerr << "accpet error: " << strerror(errno) << std::endl;
                    break;
                }
            }
        }
    }

    // 下面三次函数是处理普通IO事件就绪的回调方法
    int RecvEvent(Connection *connet)
    {
        while (true)
        {
            char buffer[128];
            int rev = recv(connet->Getfd(), buffer, sizeof(buffer) - 1, 0);
            if (rev > 0)
            {
                buffer[rev] = '\0';
                string& Inbuffer = connet->GetInbuffer();
                Inbuffer += buffer;
            }
            else if (rev == 0) // 客户端连接关闭
            {
                cout << "The client[" << UserInetWorkData_[connet->Getfd()] << "] connection has been disconnected ! ! !"
                     << endl;
                connet->GetExcepEvent()(connet);
                break;
            }
            else
            {
                if (EINTR == errno) // 读取数据时被信号的传送中断
                {
                    cout << "Reading data blocked by signal, re reading! ! !" << endl;
                    continue;
                }
                else if (EAGAIN == errno || EWOULDBLOCK == errno) // 表示缓冲区中没有可用的数据 -- 读到尾了
                {
                    cout << "No data available in the buffer! ! !" << endl;
                    break;
                }
                else // recv exception(读异常)
                {
                    cout << "recv error: " << strerror(errno) << endl;
                    connet->GetExcepEvent()(connet);
                    break;
                }
            }
        }

        vector<string> result;
        // 1.按特定的分隔符分割报文到result
        PackageSplit(connet->GetInbuffer(), &result);
        if (result.empty())
        {
            connet->GetInbuffer().clear();
            return -1;
        }
        // 2.根据result获取完整的报文到connect的inbuffer中
        for (auto &message : result)
        {
            hdf_(connet, message);
        }
    }

    int SendEvent(Connection *connet)
    {
        while (true)
        {
            int sed = send(connet->Getfd(), connet->GetOutbuffer().c_str(), connet->GetOutbuffer().size(), 0);
            if (sed > 0)
            {
                // 1.清空已经成功发送的数据 -- 可能数据太大send分段发送
                connet->GetOutbuffer().erase(0, sed);
            }
            else
            {
                if (EINTR == errno) continue; // 信号中断
                else if (EAGAIN == errno || EWOULDBLOCK == errno) break; // 发完了，但不一定outbuffer清空了, 保持EPOLLOUT就绪
                else
                {
                    cout << "send error: " << strerror(errno) << endl;
                    connet->GetExcepEvent()(connet);
                    break;
                }
            }
        }
        // 如果outbuffer真的清空了，那么去除读数据监听 -- 处理写事件第一种版本
        // if (connet->GetOutbuffer().empty())
        // {
        //     EnableReadWrite(connet->Getfd(), true, false);
        // }
        return 0;
    }

    int ExceptEvent(Connection *connet)
    {
        if (IsExists(connet->Getfd() == false)) // 判断fd是否合法
            return -1;
        // 1.去除对应fd在epoll模型中的事件监听
        if (Epoll::DelEvent(epfd_, connet->Getfd()))
        {
            cout << "Successfully removed event listening with fd = " << connet->Getfd() << "! ! !" << endl;
        }
        // 2.关闭fd文件描述符 -- 断开连接
        close(connet->Getfd());
        // 4.释放动态开辟的Connection空间
        delete Connections_[connet->Getfd()];
        // 5.去除fd在Connections_的映射关系
        auto pos = Connections_.find(connet->Getfd());
        if (pos != Connections_.end()) Connections_.erase(pos);
        cout << "Exception handling completed! ! !" << endl;
    }
 
    //=========================================================================================================================

    // 判断fd是否在Connections哈希表中，如果不存在哈希表会默认构建新的映射 -- 因为映射的值是默认的，方法里面没有处理事件的逻辑，会出错
    bool IsExists(int fd)
    {
        auto pos = Connections_.find(fd);
        if (pos == Connections_.end())
        {
            return false;
        }
        return true;
    }

    void Dispatcher()
    {
        // epoll_wait拷贝就绪队列中的数据到用户设置的缓冲区中(第二个参数), 返回值是就绪队列拷贝了多少个节点的数量
        int eventsNum = Epoll::LoopOnce(epfd_, &evEnts_[0], EVENTS_NUM);
        for (int i = 0; i < eventsNum; ++i)
        {
            int fd = evEnts_[i].data.fd;        // 事件就绪的fd
            uint32_t event = evEnts_[i].events; // 事件就绪的类型

            // 全部异常事件就绪都在读写方法事件就绪方法内部处理 -- 内部统一调用异常处理方法(ExceptEvent())
            if (event & EPOLLHUP)
                event |= (EPOLLIN | EPOLLOUT);

            if (event & EPOLLERR)
                event |= (EPOLLIN | EPOLLOUT);

            // 调用处理读事件就绪的回调函数 -- function类重载了bool，可以进行判断是否有回调函数
            if (event & EPOLLIN)
            {
                if (IsExists(fd) && Connections_[fd]->GetRecvEvent().operator bool())
                {
                    Connections_[fd]->GetRecvEvent()(Connections_[fd]); // 调用函数对象
                }
            }

            if (event & EPOLLOUT)
            {
                if (IsExists(fd) && Connections_[fd]->GetSendEvent().operator bool())
                {
                    Connections_[fd]->GetSendEvent()(Connections_[fd]);
                }
            }
        }
    }

    //================================================================================================================
public:
    // 判断特定的fd是否进行读事件和写事件的监听
    void EnableReadWrite(int fd, bool readable, bool writeable)
    {
        uint32_t event = 0;
        event |= (readable) ? EPOLLIN : 0;
        event |= (writeable) ? EPOLLOUT : 0;

        Epoll::ModEventState(epfd_, fd, event); // return ???
    }   

    const unordered_map<int, string> &GetUserInetWorkData() const { return UserInetWorkData_; }
    int Getepfd() { return epfd_; }

private:
    // Server’s port 和 ip adress
    uint16_t port_;
    string ip_;
    // listen sock fd 和 epoll fd
    int listenSockfd_;
    int epfd_;
    // fd和Connection的映射：每个fd都要有自己处理事件的回调方法和接受发送数据的缓冲区 -- 对事件进行管理
    unordered_map<int, Connection *> Connections_;
    // 就绪事件列表
    vector<epoll_event> evEnts_;
    // 建立fd和用户的ip、port映射关系(fd => ip:port)
    unordered_map<int, string> UserInetWorkData_;
    // 获取完整报文
    handler_func hdf_;
};